package com.game.framework.common.utils;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.BreakIterator;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtil {
	// Constants used by escapeHTMLTags
	private static final char[] QUOTE_ENCODE = "&quot;".toCharArray();
	private static final char[] AMP_ENCODE = "&amp;".toCharArray();
	private static final char[] LT_ENCODE = "&lt;".toCharArray();
	private static final char[] GT_ENCODE = "&gt;".toCharArray();

	/**
	 * 判断字符是否为空
	 */
	public static boolean isEmpty(String str) {
		return str == null || str.length() == 0;
	}

	/**
	 * 判断字符是否为不为空
	 */
	public static boolean isNotEmpty(String str) {
		return !isEmpty(str);
	}

	/**
	 * 判断字符是否为空或空白字符串或null组成
	 */
	public static boolean isBlank(String str) {
		return str == null || str.length() == 0 || str.trim().equals("");
	}

	/**
	 * 判断字符是否为不为空或空白字符串组成
	 */
	public static boolean isNotBlank(String str) {
		return !isBlank(str);
	}

	/**
	 * 判断字符是否为空或空白字符串,或各种null组成
	 */
	public static boolean isNull(String str) {
		return str == null || str.length() == 0 || "".equals(str.trim()) || "NULL".equalsIgnoreCase(str.trim());
	}

	/**
	 * 判断字符是否为不为空或空白字符串,或各种null组成
	 */
	public static boolean isNotNull(String str) {
		return !isNull(str);
	}

	/**
	 * 判断字符串是否可用 [isNotNull功能的升级版]
	 */
	public static boolean isAvailable(String str) {
		return !(str == null || str.length() == 0 || "".equals(str.trim())
				|| "NULL".equalsIgnoreCase(str.trim())
				|| "NULLNULL".equalsIgnoreCase(str.trim())
				|| "NULLNULLNULL".equalsIgnoreCase(str.trim()));
	}

	/**
	 * 核查字符长度
	 */
	public static boolean checkLength(String str, int minLen, int maxLen) {
		int len = 0;
		if (str != null) {
			len = str.length();
		}
		return len >= minLen && len <= maxLen;
	}

	/**
	 * 判断字符串是否是UUID字符串
	 */
	public static boolean isUUID(String str) {
		try {
			UUID.fromString(str);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 首字母转小写
	 */
	public static String toLowerCaseFirstOne(String s) {
		if (Character.isLowerCase(s.charAt(0)))
			return s;
		else
			return Character.toLowerCase(s.charAt(0)) + s.substring(1);
	}


	/**
	 * 首字母转大写
	 */
	public static String toUpperCaseFirstOne(String s) {
		if (Character.isUpperCase(s.charAt(0)))
			return s;
		else
			return Character.toUpperCase(s.charAt(0)) + s.substring(1);
	}

	/**
	 * 依据start(开始位置)和end(结束位置)截取目标字符串,包含start和end位置
	 *
	 * @param start 截取的开始位置 注:是自然位置不是下标
	 * @param end   结束位置,自然位置不是下标
	 * @param str   目标字符串
	 * @return 截取的目标字符串
	 * 参数start和end都是自然位置, 注意自然位置不包括0, 最大长度就是字符串长度..
	 * start如果小于等于0,会自动调整到开始位置,end如果超出源字符串最大长度,则自动调整为源字符串结束位置
	 */
	public static String getStr(int start, int end, String str) {
		if (start <= 0) {
			start = 1;
		}
		if (end > str.length()) {
			end = str.length();
		}
		return str.substring(start - 1, end);
	}

	/**
	 * 修改字符串中的unicode码,将unicode编码转换成原来的字符
	 *
	 * @param s 源str
	 * @return 修改后的str
	 */
	private static String decode(String s) {
		StringBuilder sb = new StringBuilder(s.length());
		char[] chars = s.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			char c = chars[i];
			if (c == '\\' && chars[i + 1] == 'u') {
				char cc = 0;
				for (int j = 0; j < 4; j++) {
					char ch = Character.toLowerCase(chars[i + 2 + j]);
					if ('0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f') {
						cc |= (Character.digit(ch, 16) << (3 - j) * 4);
					} else {
						cc = 0;
						break;
					}
				}
				if (cc > 0) {
					i += 5;
					sb.append(cc);
					continue;
				}
			}
			sb.append(c);
		}
		return sb.toString();
	}

	/**
	 * 获取修复unicode转义后的字符串  如 将字符串中的 "\u003d" 转回其原本的值
	 *
	 * @param str
	 * @return 修复后的字符串
	 */
	public static String getFixStrBecauseUnicodeEscape(String str) {
		Pattern p = Pattern.compile("(\\\\u.{4})");
		Matcher m = p.matcher(str);
		while (m.find()) {
			String xxx = m.group();
			str = str.replaceAll("\\/" + xxx, decode(xxx));
		}
		return str;
	}
	/**
	 * Map转paramsString
	 * @return name=value&name=value
	 */
	public static String mapToString(Map<String, String> params) {
		String returnString = "";
		for(String key : params.keySet()) {
			returnString += key + "=" + params.get(key) + "&";
		}
		if(!isEmpty(returnString)) {
			returnString = returnString.substring(0, returnString.length() - 1);
		}
		return returnString;
	}

	/**
	 * Map转paramsString
	 * @return name=value&name=value
	 */
	public static String mapToString(Map<Integer, Integer> params, String split1, String split2) {
		String returnString = "";
		for(Integer key : params.keySet()) {
			returnString += key + split2 + params.get(key) + split1;
		}
		if(!isEmpty(returnString)) {
			returnString = returnString.substring(0, returnString.length() - 1);
		}
		return returnString;
	}
	/**
	 * paramsString转Map
	 */
	public static Map<String, String> stringToMap(String srcString, String split1, String split2) {
		Map<String, String> map = new HashMap<>();
		String[] srcArray = srcString.split(split1);
		for(String src : srcArray) {
			String[] configs = src.split(split2);
			if(configs.length > 1) {
				map.put(configs[0], configs[1]);
			}
		}
		return map;
	}

	/**
	 * 把List<String>转换为一个用指定符号分隔的字符串
	 */
	public static String listToString(List<String> list, String sign) {
		StringBuilder sb = new StringBuilder();
		if(list != null && list.size() > 0) {
			for(int i = 0; i < list.size(); i++) {
				if(i < list.size() - 1) {
					sb.append(list.get(i) + sign);
				} else {
					sb.append(list.get(i));
				}
			}
		}
		return sb.toString();
	}

	/**
	 * 指定符号分隔的字符串转 List<String>
	 */
	public static List<String> stringToList(String srcString, String sign) {
		List<String> results = new ArrayList<>();
		if(isEmpty(srcString)) {
			return results;
		}
		String[] srcStrings = srcString.split(sign);
		for(String src : srcStrings) {
			results.add(src);
		}
		return results;
	}

	/**
	 * 数组转字符串
	 *
	 * @param srcStrings 字符串数组
	 * @param sign 分隔符
	 */
	public static String implode(Set<String> srcStrings, String sign) {
		String returnString = "";
		int i = 1;
		for(String src : srcStrings) {
			if(i == srcStrings.size()) {
				returnString += src;
			} else {
				returnString += src + ",";
			}
			i++;
		}
		return returnString;
	}

	public static String get32UUID() {
		return UUID.randomUUID().toString().trim().replaceAll("-", "");
	}

	// 根据Unicode编码完美的判断中文汉字和符号
	private static boolean isChinese(char c) {
		Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
		if(ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
				|| ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
				|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
				|| ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
				|| ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
				|| ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
				|| ub == Character.UnicodeBlock.GENERAL_PUNCTUATION) {
			return true;
		}
		return false;
	}

	/**
	 * 是否全中文
	 */
	public static boolean isChinese(String strName) {
		char[] ch = strName.toCharArray();
		for(int i = 0; i < ch.length; i++) {
			char c = ch[i];
			if(!isChinese(c)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 是否全英文
	 */
	public static boolean isEnglish(String strName) {
		char[] ch = strName.toCharArray();
		for(int i = 0; i < ch.length; i++) {
			char c = ch[i];
			if(isChinese(c) || Character.isDigit(c)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 是否中英文夹杂
	 */
	public static boolean isChineseAndEnglish(String strName) {
		boolean hasChi = false;
		boolean hasEng = false;
		char[] ch = strName.toCharArray();
		for(int i = 0; i < ch.length; i++) {
			char c = ch[i];
			if(isChinese(c)) {
				hasChi = true;
			} else {
				hasEng = true;
			}
			if(hasChi && hasEng) {
				return true;
			}
		}
		return false;
	}
	/**
	 * 字符串是否是纯数字
	 */
	public static boolean isNumeric(String str) {
		char[] ch = str.toCharArray();
		for(int i = 0; i < ch.length; i++) {
			if(!Character.isDigit(ch[i])) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 判断是否邮箱地址
	 */
	public static boolean isMail(String str){
		String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
		Pattern regex = Pattern.compile(check);
		Matcher matcher = regex.matcher(str);
		boolean isMatched = matcher.matches();
		return isMatched;
	}

	public static String getRandomStr(Integer length) {
		String all = "0123456789ABCDEFGHIJKLMNOPQISTUVWXYZabcdefghijklmnopqistuvwxyz";
		String randomStr = "";
		for (int i = 0; i < length; i++) {
			Integer j = new Random().nextInt(all.length());
			randomStr += all.charAt(j);
		}
		return randomStr;
	}

	public static String getRandomNum(Integer length) {
		String all = "0123456789";
		String randomStr = "";
		for (int i = 0; i < length; i++) {
			Integer j = new Random().nextInt(all.length());
			randomStr += all.charAt(j);
		}
		return randomStr;
	}

	/**
	 * 将 Int 数字转换为字节数组
	 */
	public static byte[] int2bytes(int src) {
		// 创建字节数组
		byte[] result = new byte[4];

		// 最低位
		result[3] = (byte)(src & 0xff);
		// 次低位
		result[2] = (byte)((src >> 8) & 0xff);
		// 次高位
		result[1] = (byte)((src >> 16) & 0xff);
		// 最高位
		result[0] = (byte)(src >>> 24);

		return result;
	}

	/**
	 * 将字节数组转换成整数值
	 */
	public static int bytes2int(byte[] src) {
		int value= 0;

		// 由高位到低位
		for (int i = 0; i < 4; i++) {
			int shift = (4 - 1 - i) * 8;
			value += (src[i] & 0x000000FF) << shift; // 往高位游
		}

		return value;
	}

	public static String substring (String src, int startIdx, int end_idx) {
		byte[] b = src.getBytes();
		String tgr = "";
		for(int i = startIdx; i <= end_idx; i++) {
			tgr += (char)b[i];
		}
		return tgr;
	}

	/**
	 * Map转String
	 */
	public static String Map2String(Map<Integer, ? extends Number> map, String split1, String split2) {
		StringBuffer sb = new StringBuffer();
		if (map == null || map.size() <= 0) {
			return "";
		}
		for (Map.Entry<Integer, ? extends Number> e : map.entrySet()) {
			sb.append(e.getKey()).append(split2).append(e.getValue()).append(split1);
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}
		return sb.toString();
	}
	/**
	 * 字符串转Map
	 */
	public static Map<Integer, Float> stringToMapByValueFloat(String srcString, String split1, String split2) {
		Map<Integer, Float> map = new HashMap<Integer, Float>();
		if (srcString == null || "".equals(srcString)) {
			return map;
		}
		String[] srcArray = srcString.split(split1);
		try {
			for (String str : srcArray) {
				String[] ss = str.split(split2);
				if (ss.length != 2) {
					continue;
				}
				map.put(Integer.parseInt(ss[0]), Float.valueOf(ss[1]));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 字符串转Map
	 */
	public static Map<Integer, Integer> stringToMapByValueInteger(String srcString, String split1, String split2) {
		Map<Integer, Integer> map = new HashMap<Integer, Integer>();
		if (srcString == null || "".equals(srcString)) {
			return map;
		}
		String[] srcArray = srcString.split(split1);
		try {
			for (String src : srcArray) {
				String[] ss = src.split(split2);
				if (ss.length != 2) {
					continue;
				}
				map.put(Integer.parseInt(ss[0]), Integer.parseInt(ss[1]));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 字符串转Map
	 */
	public static Map<Integer, Long> stringToMapByValueLong(String srcString, String split1, String split2) {
		Map<Integer, Long> map = new HashMap<Integer, Long>();
		if (srcString == null || "".equals(srcString)) {
			return map;
		}
		String[] srcArray = srcString.split(split1);
		try {
			for (String src : srcArray) {
				String[] ss = src.split(split2);
				if (ss.length != 2) {
					continue;
				}
				map.put(Integer.parseInt(ss[0]), Long.parseLong(ss[1]));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return map;
	}

	/**
	 * 特殊字符过滤
	 */
	public static boolean isCanIn(String name) {
		char[] chars = name.toCharArray();
		for (int i = 0; i < chars.length; i++) {
			// 特殊字符过滤
			if (chars[i] > 0x9fff) {
				return false;
			}
		}
		return true;
	}

	/**
	 * 检测是否有emoji字符
	 *
	 * @param source 一旦含有就抛出
	 */
	public static boolean containsEmoji(String source) {
		if (isBlank(source)) {
			return false;
		}
		for (int i = 0; i < source.length(); i++) {
			if (isEmojiCharacter(source, i)) {
				// do nothing，判断到了这里表明，确认有表情字符
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断是否是emoji字符
	 */
	private static boolean isEmojiCharacter(String substring, int index) {
		char hs = substring.charAt(index);
		if (0xd800 <= hs && hs <= 0xdbff) {
			if (substring.length() > index + 1) {
				char ls = substring.charAt(index + 1);
				int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
				if (0x1d000 <= uc && uc <= 0x1f77f) {
					return true;
				}
			}
		} else if (substring.length() > index + 1) {
			char ls = substring.charAt(index + 1);
			if (ls == 0x20e3) {
				return true;
			}
		} else {
			if (0x2100 <= hs && hs <= 0x27ff) {
				return true;
			} else if (0x2B05 <= hs && hs <= 0x2b07) {
				return true;
			} else if (0x2934 <= hs && hs <= 0x2935) {
				return true;
			} else if (0x3297 <= hs && hs <= 0x3299) {
				return true;
			} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c
					|| hs == 0x2b1b || hs == 0x2b50) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据value 排序从小到大
	 */
	public static Map<Integer, Integer> sortMapByValue(Map<Integer, Integer> oriMap) {
		Map<Integer, Integer> sortedMap = new LinkedHashMap<Integer, Integer>();
		if (oriMap != null && !oriMap.isEmpty()) {
			List<Map.Entry<Integer, Integer>> entryList = new ArrayList<Map.Entry<Integer, Integer>>(oriMap.entrySet());
			Collections.sort(entryList, new Comparator<Map.Entry<Integer, Integer>>() {
				@Override
				public int compare(Map.Entry<Integer, Integer> entry1, Map.Entry<Integer, Integer> entry2) {
					int value1 = entry1.getValue();
					int value2 = entry2.getValue();
					return value1 - value2;
				}
			});
			Iterator<Map.Entry<Integer, Integer>> iter = entryList.iterator();
			Map.Entry<Integer, Integer> tmpEntry = null;
			while (iter.hasNext()) {
				tmpEntry = iter.next();
				sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());
			}
		}
		return sortedMap;
	}

	/**
	 * 根据value 排序从大到小
	 */
	public static Map<Integer, Integer> sortMapByValue1(Map<Integer, Integer> oriMap) {
		Map<Integer, Integer> sortedMap = new LinkedHashMap<Integer, Integer>();
		if (oriMap != null && !oriMap.isEmpty()) {
			List<Map.Entry<Integer, Integer>> entryList = new ArrayList<Map.Entry<Integer, Integer>>(oriMap.entrySet());
			Collections.sort(entryList, new Comparator<Map.Entry<Integer, Integer>>() {
				@Override
				public int compare(Map.Entry<Integer, Integer> entry1, Map.Entry<Integer, Integer> entry2) {
					int value1 = entry1.getValue();
					int value2 = entry2.getValue();
					return value2 - value1;
				}
			});
			Iterator<Map.Entry<Integer, Integer>> iter = entryList.iterator();
			Map.Entry<Integer, Integer> tmpEntry = null;
			while (iter.hasNext()) {
				tmpEntry = iter.next();
				sortedMap.put(tmpEntry.getKey(), tmpEntry.getValue());
			}
		}
		return sortedMap;
	}

	/**
	 * Replaces all instances of oldString with newString in string.
	 */
	public static String replace(String string, String oldString, String newString) {
		if (string == null) {
			return null;
		}
		int i = 0;
		// Make sure that oldString appears at least once before doing any
		// processing.
		if ((i = string.indexOf(oldString, i)) >= 0) {
			// Use char []'s, as they are more efficient to deal with.
			char[] string2 = string.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(string2.length);
			buf.append(string2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			// Replace all remaining instances of oldString with newString.
			while ((i = string.indexOf(oldString, i)) > 0) {
				buf.append(string2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(string2, j, string2.length - j);
			return buf.toString();
		}
		return string;
	}

	/**
	 * Replaces all instances of oldString with newString in line with the added
	 * feature that matches of newString in oldString ignore case.
	 */
	public static String replaceIgnoreCase(String line, String oldString, String newString) {
		if (line == null) {
			return null;
		}
		String lcLine = line.toLowerCase();
		String lcOldString = oldString.toLowerCase();
		int i = 0;
		if ((i = lcLine.indexOf(lcOldString, i)) >= 0) {
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = lcLine.indexOf(lcOldString, i)) > 0) {
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			return buf.toString();
		}
		return line;
	}

	/**
	 * Replaces all instances of oldString with newString in line with the added
	 * feature that matches of newString in oldString ignore case. The count
	 * paramater is set to the number of replaces performed.
	 */
	public static String replaceIgnoreCase(String line, String oldString, String newString, int[] count) {
		if (line == null) {
			return null;
		}
		String lcLine = line.toLowerCase();
		String lcOldString = oldString.toLowerCase();
		int i = 0;
		if ((i = lcLine.indexOf(lcOldString, i)) >= 0) {
			int counter = 1;
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = lcLine.indexOf(lcOldString, i)) > 0) {
				counter++;
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			count[0] = counter;
			return buf.toString();
		}
		return line;
	}

	/**
	 * Replaces all instances of oldString with newString in line. The count
	 * Integer is updated with number of replaces.
	 */
	public static String replace(String line, String oldString, String newString, int[] count) {
		if (line == null) {
			return null;
		}
		int i = 0;
		if ((i = line.indexOf(oldString, i)) >= 0) {
			int counter = 1;
			char[] line2 = line.toCharArray();
			char[] newString2 = newString.toCharArray();
			int oLength = oldString.length();
			StringBuilder buf = new StringBuilder(line2.length);
			buf.append(line2, 0, i).append(newString2);
			i += oLength;
			int j = i;
			while ((i = line.indexOf(oldString, i)) > 0) {
				counter++;
				buf.append(line2, j, i - j).append(newString2);
				i += oLength;
				j = i;
			}
			buf.append(line2, j, line2.length - j);
			count[0] = counter;
			return buf.toString();
		}
		return line;
	}

	/**
	 * This method takes a string and strips out all tags except <br>
	 * tags while still leaving the tag body intact.
	 */
	public static String stripTags(String in) {
		if (in == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = in.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i + 3 < len && input[i + 1] == 'b' && input[i + 2] == 'r' && input[i + 3] == '>') {
					i += 3;
					continue;
				}
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
			} else if (ch == '>') {
				last = i + 1;
			}
		}
		if (last == 0) {
			return in;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * This method takes a string which may contain HTML tags (ie, &lt;b&gt;,
	 * &lt;table&gt;, etc) and converts the '&lt'' and '&gt;' characters to
	 * their HTML escape sequences. It will also replace LF with &lt;br&gt;.
	 */
	public static String escapeHTMLTags(String in) {
		if (in == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = in.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(LT_ENCODE);
			} else if (ch == '>') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(GT_ENCODE);
			} else if (ch == '\n') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append("<br>");
			}
		}
		if (last == 0) {
			return in;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * Used by the hash method.
	 */
	private static Map<String, MessageDigest> digests = new ConcurrentHashMap<String, MessageDigest>();

	/**
	 * 
	 * @param data
	 *            the String to compute the hash of.
	 * @return a hashed version of the passed-in String
	 */
	public static String hash(String data) {
		return hash(data, "MD5");
	}

	/**
	 * Hashes a String using the specified algorithm and returns the result as a
	 * String of hexadecimal numbers. This method is synchronized to avoid
	 * excessive MessageDigest object creation. If calling this method becomes a
	 * bottleneck in your code, you may wish to maintain a pool of MessageDigest
	 * objects instead of using this method.
	 */
	public static String hash(String data, String algorithm) {
		try {
			return hash(data.getBytes("utf-8"), algorithm);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return data;
	}

	/**
	 * Hashes a byte array using the specified algorithm and returns the result
	 * as a String of hexadecimal numbers. This method is synchronized to avoid
	 * excessive MessageDigest object creation. If calling this method becomes a
	 * bottleneck in your code, you may wish to maintain a pool of MessageDigest
	 * objects instead of using this method.
	 */
	public static String hash(byte[] bytes, String algorithm) {
		synchronized (algorithm.intern()) {
			MessageDigest digest = digests.get(algorithm);
			if (digest == null) {
				try {
					digest = MessageDigest.getInstance(algorithm);
					digests.put(algorithm, digest);
				} catch (NoSuchAlgorithmException nsae) {
					
					return null;
				}
			}
			// Now, compute hash.
			digest.update(bytes);
			return encodeHex(digest.digest());
		}
	}

	/**
	 * Turns an array of bytes into a String representing each byte as an
	 * unsigned hex number.
	 */
	public static String encodeHex(byte[] bytes) {
		StringBuilder buf = new StringBuilder(bytes.length * 2);
		int i;

		for (i = 0; i < bytes.length; i++) {
			if (((int) bytes[i] & 0xff) < 0x10) {
				buf.append("0");
			}
			buf.append(Long.toString((int) bytes[i] & 0xff, 16));
		}
		return buf.toString();
	}

	/**
	 * Turns a hex encoded string into a byte array. It is specifically meant to
	 * "reverse" the toHex(byte[]) method.
	 */
	public static byte[] decodeHex(String hex) {
		char[] chars = hex.toCharArray();
		byte[] bytes = new byte[chars.length / 2];
		int byteCount = 0;
		for (int i = 0; i < chars.length; i += 2) {
			int newByte = 0x00;
			newByte |= hexCharToByte(chars[i]);
			newByte <<= 4;
			newByte |= hexCharToByte(chars[i + 1]);
			bytes[byteCount] = (byte) newByte;
			byteCount++;
		}
		return bytes;
	}

	/**
	 * Returns the the byte value of a hexadecmical char (0-f). It's assumed
	 * that the hexidecimal chars are lower case as appropriate.
	 */
	private static byte hexCharToByte(char ch) {
		byte ret = 0x00;
		switch (ch) {
			case '0':
				ret = 0x00;
				break;
			case '1':
				ret =  0x01;
				break;
			case '2':
				ret =  0x02;
				break;
			case '3':
				ret =  0x03;
				break;
			case '4':
				ret =  0x04;
				break;
			case '5':
				ret =  0x05;
				break;
			case '6':
				ret =  0x06;
				break;
			case '7':
				ret =  0x07;
				break;
			case '8':
				ret =  0x08;
				break;
			case '9':
				ret =  0x09;
				break;
			case 'a':
				ret =  0x0A;
				break;
			case 'b':
				ret =  0x0B;
				break;
			case 'c':
				ret =  0x0C;
				break;
			case 'd':
				ret =  0x0D;
				break;
			case 'e':
				ret =  0x0E;
				break;
			case 'f':
				ret =  0x0F;
				break;
			default:
				break;
		}
		return ret;
	}




	/**
	 * Converts a line of text into an array of lower case words using a
	 * BreakIterator.wordInstance().
	 */
	public static String[] toLowerCaseWordArray(String text) {
		if (text == null || text.length() == 0) {
			return new String[0];
		}

		List<String> wordList = new ArrayList<String>();
		BreakIterator boundary = BreakIterator.getWordInstance();
		boundary.setText(text);
		int start = 0;

		for (int end = boundary.next(); end != BreakIterator.DONE; start = end, end = boundary.next()) {
			String tmp = text.substring(start, end).trim();
			// Remove characters that are not needed.
			tmp = replace(tmp, "+", "");
			tmp = replace(tmp, "/", "");
			tmp = replace(tmp, "\\", "");
			tmp = replace(tmp, "#", "");
			tmp = replace(tmp, "*", "");
			tmp = replace(tmp, ")", "");
			tmp = replace(tmp, "(", "");
			tmp = replace(tmp, "&", "");
			if (tmp.length() > 0) {
				wordList.add(tmp);
			}
		}
		return wordList.toArray(new String[wordList.size()]);
	}

	/**
	 * Pseudo-random number generator object for use with randomString(). The
	 * Random class is not considered to be cryptographically secure, so only
	 * use these random Strings for low to medium security applications.
	 */
	private static Random randGen = new Random();

	/**
	 * Array of numbers and letters of mixed case. Numbers appear in the list
	 * twice so that there is a more equal chance that a number will be picked.
	 * We can use the array to get a random number or letter by picking a random
	 * array index.
	 */
	private static char[] numbersAndLetters = ("0123456789abcdefghijklmnopqrstuvwxyz" + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").toCharArray();

	/**
	 * Returns a random String of numbers and letters (lower and upper case) of
	 * the specified length. The method uses the Random class that is built-in
	 * to Java which is suitable for low to medium grade security uses. This
	 * means that the output is only pseudo random, i.e., each number is
	 * mathematically generated so is not truly random.
	 */
	public static String randomString(int length) {
		if (length < 1) {
			return null;
		}
		// Create a char buffer to put random letters and numbers in.
		char[] randBuffer = new char[length];
		for (int i = 0; i < randBuffer.length; i++) {
			randBuffer[i] = numbersAndLetters[randGen.nextInt(71)];
		}
		return new String(randBuffer);
	}

	/**
	 * Intelligently chops a String at a word boundary (whitespace) that occurs
	 * at the specified index in the argument or before. However, if there is a
	 * newline character before <code>length</code>, the String will be chopped
	 * there. If no newline or whitespace is found in <code>string</code> up to
	 * the index <code>length</code>, the String will chopped at
	 */
	public static String chopAtWord(String string, int length) {
		if (string == null || string.length() == 0) {
			return string;
		}

		char[] charArray = string.toCharArray();
		int sLength = string.length();
		if (length < sLength) {
			sLength = length;
		}
		// First check if there is a newline character before length; if so,
		// chop word there.
		for (int i = 0; i < sLength - 1; i++) {
			// Windows
			if (charArray[i] == '\r' && charArray[i + 1] == '\n') {
				return string.substring(0, i + 1);
			}
			// Unix
			else if (charArray[i] == '\n') {
				return string.substring(0, i);
			}
		}
		// Also check boundary case of Unix newline
		if (charArray[sLength - 1] == '\n') {
			return string.substring(0, sLength - 1);
		}
		// Done checking for newline, now see if the total string is less than
		// the specified chop point.
		if (string.length() < length) {
			return string;
		}
		// No newline, so chop at the first whitespace.
		for (int i = length - 1; i > 0; i--) {
			if (charArray[i] == ' ') {
				return string.substring(0, i).trim();
			}
		}
		// Did not find word boundary so return original String chopped at
		// specified length.
		return string.substring(0, length);
	}

	/**
	 * Reformats a string where lines that are longer than <tt>width</tt> are
	 * split apart at the earliest wordbreak or at maxLength, whichever is
	 * sooner. If the width specified is less than 5 or greater than the input
	 * Strings length the string will be returned as is.
	 */
	public static String escapeForSQL(String string) {
		if (string == null) {
			return null;
		} else if (string.length() == 0) {
			return string;
		}
		char ch;
		char[] input = string.toCharArray();
		int i = 0;
		int last = 0;
		int len = input.length;
		StringBuilder out = null;
		for (; i < len; i++) {
			ch = input[i];
			if (ch == '\'') {
				if (out == null) {
					out = new StringBuilder(len + 2);
				}
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append('\'').append('\'');
			}
		}
		if (out == null) {
			return string;
		} else if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}

	/**
	 * Escapes all necessary characters in the String so that it can be used in
	 * an XML doc.
	 */
	public static String escapeForXML(String string) {
		if (string == null) {
			return null;
		}
		char ch;
		int i = 0;
		int last = 0;
		char[] input = string.toCharArray();
		int len = input.length;
		StringBuilder out = new StringBuilder((int) (len * 1.3));
		for (; i < len; i++) {
			ch = input[i];
			if (ch > '>') {
			} else if (ch == '<') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(LT_ENCODE);
			} else if (ch == '&') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(AMP_ENCODE);
			} else if (ch == '"') {
				if (i > last) {
					out.append(input, last, i - last);
				}
				last = i + 1;
				out.append(QUOTE_ENCODE);
			}
		}
		if (last == 0) {
			return string;
		}
		if (i > last) {
			out.append(input, last, i - last);
		}
		return out.toString();
	}
	/**
	 * Unescapes the String by converting XML escape sequences back into normal
	 * characters.
	 */
	public static String unescapeFromXML(String string) {
		string = replace(string, "&lt;", "<");
		string = replace(string, "&gt;", ">");
		string = replace(string, "&quot;", "\"");
		return replace(string, "&amp;", "&");
	}

	private static final char[] zeroArray = "0000000000000000000000000000000000000000000000000000000000000000".toCharArray();

	/**
	 * Pads the supplied String with 0's to the specified length and returns the
	 * result as a new String. For example, if the initial String is "9999" and
	 * the desired length is 8, the result would be "00009999". This type of
	 * padding is useful for creating numerical values that need to be stored
	 * and sorted as character tmp. Note: the current implementation of this
	 * method allows for a maximum <tt>length</tt> of 64.
	 */
	public static String zeroPadString(String string, int length) {
		if (string == null || string.length() > length) {
			return string;
		}
		StringBuilder buf = new StringBuilder(length);
		buf.append(zeroArray, 0, length - string.length()).append(string);
		return buf.toString();
	}
	/**
	 * Formats a Date as a fifteen character long String made up of the Date's
	 * padded millisecond value.
	 */
	public static String dateToMillis(Date date) {
		return zeroPadString(Long.toString(date.getTime()), 15);
	}
	/**
	 * Returns a collection of Strings as a comma-delimitted list of strings.
	 */
	public static String collectionToString(Collection<String> collection) {
		if (collection == null || collection.isEmpty()) {
			return "";
		}
		StringBuilder buf = new StringBuilder();
		String delim = "";
		for (String element : collection) {
			buf.append(delim);
			buf.append(element);
			delim = ",";
		}
		return buf.toString();
	}
	/**
	 * Returns a comma-delimitted list of Strings as a Collection.
	 */
	public static Collection<String> stringToCollection(String string) {
		if (string == null || string.trim().length() == 0) {
			return Collections.emptyList();
		}
		Collection<String> collection = new ArrayList<String>();
		StringTokenizer tokens = new StringTokenizer(string, ",");
		while (tokens.hasMoreTokens()) {
			collection.add(tokens.nextToken().trim());
		}
		return collection;
	}

	/**
	 * Abbreviates a string to a specified length and then adds an ellipsis if
	 * the input is greater than the maxWidth.
	 */
	public static String abbreviate(String str, int maxWidth) {
		if (null == str) {
			return null;
		}
		if (str.length() <= maxWidth) {
			return str;
		}
		return str.substring(0, maxWidth) + "...";
	}

	public static byte[] writeStrUTF(String str) {
		if (str == null){ str = "";}
		int strlen = str.length();
		int utflen = 0;
		int c, count = 0;
		/* use charAt instead of copying String to char array */
		for (int i = 0; i < strlen; i++) {
			c = str.charAt(i);
			if ((c >= 0x0001) && (c <= 0x007F)) {
				utflen++;
			} else if (c > 0x07FF) {
				utflen += 3;
			} else {
				utflen += 2;
			}
		}
		if (utflen > 165535) {
			return null;
		}
		byte[] bytearr = new byte[utflen + 2];
		bytearr[count++] = (byte) ((utflen >>> 8) & 0xFF);
		bytearr[count++] = (byte) ((utflen >>> 0) & 0xFF);
		int i = 0;
		for (i = 0; i < strlen; i++) {
			c = str.charAt(i);
			if (!((c >= 0x0001) && (c <= 0x007F))){ break;}
			bytearr[count++] = (byte) c;
		}
		for (; i < strlen; i++) {
			c = str.charAt(i);
			if ((c >= 0x0001) && (c <= 0x007F)) {
				bytearr[count++] = (byte) c;
			} else if (c > 0x07FF) {
				bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
				bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
				bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
			} else {
				bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
				bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
			}
		}
		return bytearr;
	}


	/**
	 * Concatenates strings.
	 */
	public static String concat(final String... strings) {
		final StringBuilder sbString = new StringBuilder(getLength(strings));
		for (final String string : strings) {
			sbString.append(string);
		}
		return sbString.toString();
	}

	/**
	 * Creates new string builder with size initializated to
	 * <code>sizeHint</code>, unless total length of strings is greater than
	 * <code>sizeHint</code>.
	 */
	public static StringBuilder startAppend(final int sizeHint, final String... strings) {
		final int length = getLength(strings);
		final StringBuilder sbString = new StringBuilder(sizeHint > length ? sizeHint : length);
		for (final String string : strings) {
			sbString.append(string);
		}
		return sbString;
	}

	/**
	 * Appends strings to existing string builder.
	 */
	public static void append(final StringBuilder sbString, final String... strings) {
		sbString.ensureCapacity(sbString.length() + getLength(strings));
		for (final String string : strings) {
			sbString.append(string);
		}
	}
	/**
	 * Counts total length of all the strings.
	 */
	private static int getLength(final String[] strings) {
		int length = 0;
		for (final String string : strings) {
			if (string == null) {
				length += 4;
			}else {
				length += string.length();
			}
		}
		return length;
	}

	/**
	 * @param data
	 * @param place 位置 从0 开始
	 * @param sgin 标记 true or false
	 */
	public static int setValue(int data, int place, boolean sgin) {
		if (sgin) {
			return data | (1 << place);
		}
		else {
			return data & ~(1 << place);
		}

	}

	public static String trim(String str) {
		if (str == null) {
			str = "";
		} else {
			str = str.trim();
		}
		if (str.length() == 0) {
			return str;
		}
		if (str.charAt(0) == '"') {
			str = str.substring(1);
		}
		if (str.charAt(str.length() - 1) == '"') {
			str = str.substring(0, str.length() - 1);
		}
		return str;
	}

	public static String[] getStringList(String str) {
		str = trim(str);
		if (str.endsWith(",")) {
			str = str.substring(0, str.length() - 1);
		}
		String sep = ",";
		if (str.indexOf(':') >= 0) {
			sep = ":";
		}
		return str.split(sep);
	}

	public static String[] getStringList(String str, String sep) {
		str = trim(str);
		return str.split(sep);
	}

	public static int[] getIntArray(String str, String sep) {
		String[] prop = getStringList(str, sep);
		List<Integer> tmp = new ArrayList<Integer>();
		if (isEmpty(str)) {
			return new int[0];
		}
		for (int i = 0; i < prop.length; i++) {
			try {
				int r = Integer.parseInt(prop[i]);
				tmp.add(r);
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
		int[] ints = new int[tmp.size()];
		for (int i = 0; i < tmp.size(); i++) {
			ints[i] = tmp.get(i);
		}
		return ints;
	}

	public static Long[] getLongArray(String str, String sep) {
		List<Long> tmp = new ArrayList<Long>();
		if (isEmpty(str)) {
			return null;
		}
		String[] prop = getStringList(str, sep);
		for (int i = 0; i < prop.length; i++) {
			long r = Long.parseLong(prop[i]);
			tmp.add(r);
		}
		return tmp.toArray(new Long[]{});
	}

	public static List<Integer> getIntList(String str, String sep) {
		List<Integer> tmp = new ArrayList<Integer>();
		if (isEmpty(str)) {
			return tmp;
		}
		String[] prop = getStringList(str, sep);
		for (int i = 0; i < prop.length; i++) {
			int r = Integer.parseInt(prop[i]);
			tmp.add(r);
		}
		return tmp;
	}

	public static String join(short[] strs, String sep) {
		if(strs == null||strs.length<1){ return "";}
		StringBuffer buffer = new StringBuffer();
		buffer.append(strs[0]);
		for (int i = 1; i < strs.length; i++) {
			buffer.append(sep).append(strs[i]);
		}
		return buffer.toString();
	}

	public static String join(long[] strs, String sep) {
		if(strs == null||strs.length<1){ return "";}
		StringBuffer buffer = new StringBuffer();
		buffer.append(strs[0]);
		for (int i = 1; i < strs.length; i++) {
			buffer.append(sep).append(strs[i]);
		}
		return buffer.toString();
	}

	public static String join(int[] strs, String sep) {
		if(strs == null||strs.length<1) {return "";}
		StringBuffer buffer = new StringBuffer();
		buffer.append(strs[0]);
		for (int i = 1; i < strs.length; i++) {
			buffer.append(sep).append(strs[i]);
		}
		return buffer.toString();
	}

	public static String join(String[] strs, String sep) {
		if(strs == null||strs.length<1){ return "";}
		StringBuffer buffer = new StringBuffer();
		buffer.append(strs[0]);
		for (int i = 1; i < strs.length; i++) {
			buffer.append(sep).append(strs[i]);
		}
		return buffer.toString();
	}

	public static String join(Collection<? extends Object> objs, String sep) {
		if(objs == null||objs.size()<1) { return "";}
		StringBuffer sb = new StringBuffer();
		boolean notFirst = false;
		for(Object obj : objs){
			if(notFirst){
				sb.append(sep);
			}
			sb.append(obj);
			notFirst = true;
		}
		return sb.toString();
	}

	public static double[] getDoubleList(String str) {
		String[] prop = getStringList(str);
		double[] ds = new double[prop.length];
		for (int i = 0; i < ds.length; i++) {
			ds[i] = Double.parseDouble(prop[i]);
		}
		return ds;
	}

	public static List<String> getListBySplit(String str, String split) {
		List<String> list = new ArrayList<String>();
		if (str == null || "".equalsIgnoreCase(str.trim())) {
			return null;
		}
		String[] strs = str.split(split);
		for (String temp : strs) {
			if (temp != null && !"".equalsIgnoreCase(temp.trim())) {
				list.add(temp.trim());
			}
		}
		return list;
	}

	/**
	 * 阵型字符串数组转换成阵型数组
	 */
	public static int[] getIntList(String[] str) {
		int[] ints = new int[str.length];
		for(int i=0;i<str.length;i++){
			ints[i] = Integer.parseInt(str[i]);
		}
		return ints;
	}

	public static int[] getIntList(String str) {
		String[] prop = getStringList(str);
		List<Integer> tmp = new ArrayList<Integer>();
		for (int i = 0; i < prop.length; i++) {
			try {
				String sInt = prop[i].trim();
				if (sInt.length() < 20) {
					int r = Integer.parseInt(prop[i].trim());
					tmp.add(r);
				}
			} catch (Exception e) {
			}
		}
		int[] ints = new int[tmp.size()];
		for (int i = 0; i < tmp.size(); i++) {
			ints[i] = tmp.get(i);
		}
		return ints;

	}

	public static String toWrapString(Object obj, String content) {
		if (obj == null) {
			return "null";
		} else {
			return obj.getClass().getName() + "@" + obj.hashCode() + "[\r\n"
					+ content + "\r\n]";
		}
	}

	// 将1,2,3和{1,2,3}格式的字符串转化为JDK的bitset
	// 考虑了两边是否有{}，数字两边是否有空格，是否合法数字
	public static BitSet bitSetFromString(String str) {
		if (str == null) {
			return new BitSet();
		}
		if (str.startsWith("{")) {
			str = str.substring(1);
		}
		if (str.endsWith("}")) {
			str = str.substring(0, str.length() - 1);
		}
		int[] ints = getIntList(str);
		BitSet bs = new BitSet();
		for (int i : ints) {
			bs.set(i);
		}
		return bs;
	}

	public static boolean hasExcludeChar(String str) {
		if (str != null) {
			char[] chs = str.toCharArray();
			for (int i = 0; i < chs.length; i++) {
				if (Character.getType(chs[i]) == Character.PRIVATE_USE) {
					return true;
				}

			}
		}
		return false;
	}

	public static String replaceSql(String str) {
		if (str != null) {
			return str.replaceAll("'", "’").replaceAll("<", "&lt;")
					.replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
		}
		return "";
	}
	/**
	 * 判断两个字符串是否相等
	 *
	 * @return true,字符串相等;false,字符串不相等
	 */
	public static boolean isEquals(String s1, String s2) {
		if (s1 != null) {
			return s1.equals(s2);
		}
		if (s2 != null) {
			return false;
		}
		// 两个字符串都是null
		return true;
	}
	/**
	 * 判断字符串是否时数字
	 */
	public static boolean isDigit(String text) {
		String reg = "[-]*[\\d]+[\\.\\d+]*";
		Pattern pat = Pattern.compile(reg);
		Matcher mat = pat.matcher(text);
		return mat.matches();
	}
	/**
	 * 判断是不是数字
	 */
	public static boolean isNumber(String str) {
		if (isBlank(str)) {
			return false;
		}
		return isNumeric(str);
	}
	/**
	 * 判断一句话是否是汉语
	 */
	public static boolean isChiness(String text) {
		String reg = "[\\w]*[\\u4e00-\\u9fa5]+[\\w]*";
		Pattern pat = Pattern.compile(reg);
		Matcher mat = pat.matcher(text);
		boolean result = mat.matches();
		return result;
	}
	/**
	 * utf8mb64使用4个字节而不是3个字节
	 */
	public static boolean isUTF8mb64(String content) {
		if (content == null || content.isEmpty()) {
			return false;
		}
		char[] chars = new char[content.length()];
		content.getChars(0, content.length(), chars, 0);
		//utf-8最大编码范围
		int maxValue = 0x10ffff;
		//是否需要编码
		boolean key = false;
		long value = 0;
		for (char c : chars) {
			value=c;
			if (value > maxValue) {
				key = true;
				break;
			}
		}
		return key;
	}

	/**
	 * base64加密
	 */
	public static String base64Encode(String content) {
		if (content == null || content.isEmpty()) {
			return "";
		}
		try {
			return Base64.encodeBase64String(content.getBytes("utf8"));
		} catch (UnsupportedEncodingException e) {
			LoggerFactory.getLogger(StringUtil.class).error(
					"not supported encode", e);
			return "";
		}
	}

	/**
	 * base64解密
	 */
	public static String base64Decode(String content) {
		if (content == null || content.isEmpty()) {
			return "";
		}
		// 需要
		try {
			return new String(Base64.decodeBase64(content), "utf8");
		} catch (UnsupportedEncodingException e) {
			LoggerFactory.getLogger(StringUtil.class).error(
					"not supported encode", e);
			return "";
		}
	}

	/**
	 * 判断单个字符是否是汉语
	 */
	public static boolean isChineseChar(char cha) {
		String reg = "[\\u4e00-\\u9fa5,\\uF900-\\uFA2D]";
		Pattern pat = Pattern.compile(reg);
		String text = Character.toString(cha);
		Matcher mat = pat.matcher(text);
		boolean result = mat.matches();
		return result;
	}

	
	public static boolean isUtf8Char(char cha) {
		String reg = "[\\u0000-\\uffff]";
		Pattern pat = Pattern.compile(reg);
		String text = Character.toString(cha);
		Matcher mat = pat.matcher(text);
		boolean result = mat.matches();
		return result;
	}
	
	/**
	 * 判断字符是否是字母(包括大小写)或者数字
	 */
	public static boolean isLetterAndDigit(String cha) {
		String reg = "[\\w]+";
		Pattern pat = Pattern.compile(reg);
		Matcher mat = pat.matcher(cha);
		boolean result = mat.matches();
		return result;
	}
	
	/**
	 * 判断字符是否是字母(包括大小写)或者数字
	 */
	public static boolean isLetterAndDigit(char cha) {
		return isLetterAndDigit(""+cha);
	}
	
	/**
	 * 获取字符串长度 中文算2个字符
	 */
	public static int getStringCount(String test) {
		int count = 0;
		for (int i = 0; i < test.length(); i++) {
			char cha = test.charAt(i);
			count++;
			if (isChineseChar(cha)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * 返回字符串中汉字的数量
	 */
	public static int getChineseCount(String test) {
		int count = 0;
		for (int i = 0; i < test.length(); i++) {
			char cha = test.charAt(i);
			if (isChineseChar(cha)) {
				count++;
			}
		}
		return count;
	}

	/**
	 * 返回字符串中字母和数字的个数，其中字母包括大小写
	 */
	public static int getLetterAndDigitCount(String text) {
		int count = 0;
		boolean tempResult = false;
		for (int i = 0; i < text.length(); i++) {
			tempResult = isLetterAndDigit(text);
			if (tempResult) {
				count++;
			}
		}
		return count;
	}

	/**
	 * 将字符串首字母大写
	 */
	public static String upperCaseFirstCharOnly(String s) {
		if (s == null || s.length() < 1) {
			return s;
		}
		return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
	}

	/**
	 * 截取字符串
	 */
	public static String subLastChar(String s) {
		if (s != null && s.length() > 0)
		{
			return s.substring(0, s.length() - 1);
		}
		return s;
	}

	/**
	 * 使用解码的方式检查指定的字符串中是有效的Mysql支持的UTF-8编码
	 * @return true,有效的;false,无效的
	 */
	public static boolean isValidMySqlUTF8(String str) {
		byte[] strBytes;
		try {
			strBytes = str.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
		final int length = strBytes.length;
		for (int i = 0; i < length;) {
			final int c = 0xFF & strBytes[i];
			do {
				if (c < 0x80) {
					i++;
					break;
				}
				if (c < 0xc2) {
					return false;
				}
				if (c < 0xe0) {
					final int c1Index = i + 1;
					if (c1Index >= length) {
						return false;
					}
					final int c1 = 0xFF & strBytes[c1Index];
					if (!((c1 ^ 0x80) < 0x40)) {
						return false;
					}
					i += 2;
					break;
				}
				if (c < 0xf0) {
					final int c1Index = i + 1;
					final int c2Index = c1Index + 1;
					if (c2Index >= length) {
						return false;
					}
					final int c1 = 0xFF & strBytes[c1Index];
					final int c2 = 0xFF & strBytes[c2Index];
					if (!((c1 ^ 0x80) < 0x40 && (c2 ^ 0x80) < 0x40 && (c >= 0xe1 || c1 >= 0xa0))) {
						return false;
					}
					i += 3;
					break;
				}
				return false;
			} while (false);
		}
		return true;
	}

	/**
	 * 检查指定的字符串中是有效的Mysql支持的UTF-8编码,该检测直接比较每个code point是否在0X0000~0XFFFF中(Mysql
	 * utf8能够有效支持的UNICODE编码范围0X000~0XFFFF)
	 */
	public static boolean isValidMySqlUTF8Fast(String str) {
		final int length = str.length();
		for (int i = 0; i < length; i++) {
			int c = str.codePointAt(i);
			if (c < 0X0000 || c > 0Xffff) {
				return false;
			}
		}
		return true;
	}
	/**
	 * 百家姓
	 */
	public static final String XS = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯昝管卢莫经房裘缪干解应宗丁宣贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄曲家封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗山谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘景詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲邰从鄂索咸籍赖卓蔺屠蒙池乔阴鬱胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍卻璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴瞿阎充慕连茹习宦艾鱼容向古易慎戈廖庾终暨居衡步都耿满弘匡国文寇广禄阙东欧殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾毋沙乜养鞠须丰巢关蒯相查后荆红游竺权逯盖益桓公万俟司马上官欧阳夏侯诸葛闻人东方赫连皇甫尉迟公羊澹台公冶宗政濮阳淳于单于太叔申屠公孙仲孙轩辕令狐钟离宇文长孙慕容鲜于闾丘司徒司空丌官司寇仉督子车颛孙端木巫马公西漆雕乐正壤驷公良拓跋夹谷宰父谷梁晋楚闫法汝鄢涂钦段干百里东郭南门呼延归海羊舌微生岳帅缑亢况郈有琴梁丘左丘东门西门商牟佘佴伯赏南宫墨哈谯笪年爱阳佟";
	public static final String BASE = "abcdefghijklmnopqrstuvwxyz0123456789";
	public static final String BASE_MIN = "abcdefghijkmnpqrtuvwxyz0123456789";
	public static final String BASE_EX = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
	public static int[] months = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
	private static Random random = new Random();

	/**
	 * 获取简单的中文名字
	 */
	public static String getSimpleChineseName() {
		StringBuilder sb = new StringBuilder();
		sb.append(XS.charAt(new Random().nextInt(XS.length()))).append(getRandomChineseChar()).append(getRandomChineseChar());
		return sb.toString();
	}

	/**
	 * 获取随机年龄,一般用来做测试数据用
	 *
	 * 随机年龄 返回的年龄数字在10-99
	 */
	public static int getRandomAge() {
		return Integer.parseInt(getNum(2));
	}

	/**
	 * 获取随机生日字符串,返回yyyyMMdd格式的日期字符串
	 *
	 * 随机的生日字符串, yyyyMMdd格式
	 * 返回的年龄区间在1910-2018年之间,暂不包含2018
	 * Random().nextInt(10) 会返回0-9之间的随机整数,包括0或9,但不包括10
	 */
	public static String getRandomBirthdayString() {
		int year = 1910 + random.nextInt(108);
		int month = months[random.nextInt(12)];
		int day = 1 + random.nextInt(28);

		StringBuilder stb = new StringBuilder();
		stb.append(year);
		if (month < 10) {
			stb.append("0").append(month);
		} else {
			stb.append(month);
		}
		if (day < 10) {
			stb.append("0").append(day);
		} else {
			stb.append(day);
		}
		return stb.toString();
	}

	/**
	 * 获取随机身份证号,注意该身份证号只是大致看着相似,用来测试用,严格的身份证号校验是通不过的
	 *
	 * 随机的18位身份证号
	 * 严格的身份证号校验是通不过的
	 */
	public static String getRandomIDNo() {
		// RegUtil 如不想引用 可以用随机的两位数字替代前两位
		Object[] array = {"0","1","2","3","4","5","6","7","8","9"};
		String areacode = (String) array[random.nextInt(array.length)];
		StringBuilder stb = new StringBuilder(areacode);
		stb.append(getNum(4)).append(getRandomBirthdayString()).append(getNum(4));
		return stb.toString();

	}

	/**
	 * 获取随机手机号,注意有可能在在严格的手机号验证中通不过
	 *
	 * 随机手机号
	 */
	public static String getRandomMobileNumber() {
		StringBuilder stb = new StringBuilder();
		stb.append(1).append(3 + random.nextInt(7)).append(getNum(9));
		return stb.toString();
	}

	/**
	 * 获取一段指定长度首字母不为0的随机数字
	 *
	 * @param count 要获取的随机数字的位数
	 * @return 随机数字组成的字符串
	 * 首字母时
	 */
	public static String getRandomNumber(int count) {
		return getNum(count);
	}

	public static String getRandomStr(int length) {
		if (length <= 0) {
			return "";
		}
		int randomNum;
		char randomChar;
		// StringBuffer类型的可以append增加字符
		StringBuffer stb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			// 可生成[0,n)之间的整数，获得随机位置
			randomNum = random.nextInt(BASE_MIN.length());
			// 获得随机位置对应的字符
			randomChar = BASE_MIN.charAt(randomNum);
			// 组成一个随机字符串
			stb.append(randomChar);
		}
		return stb.toString();
	}

	/**
	 * 单个常见汉字字符
	 * 随机生成单个常见汉字
	 */
	public static String getRandomChineseChar() {
		String str = "";
		int highCode;
		int lowCode;
		Random random = new Random();
		highCode = (176 + Math.abs(random.nextInt(39))); // B0 + 0~39(16~55) 一级汉字所占区
		lowCode = (161 + Math.abs(random.nextInt(93))); // A1 + 0~93 每区有94个汉字
		byte[] b = new byte[2];
		b[0] = (Integer.valueOf(highCode)).byteValue();
		b[1] = (Integer.valueOf(lowCode)).byteValue();
		try {
			str = new String(b, "GBK");
		} catch (Exception e) {
			e.printStackTrace();
		}
		return str;
	}

	/**
	 * 获取一段指定长度首字母不为0的随机数字
	 *
	 * count 要获取的随机数字的位数
	 * 随机数字组成的字符串
	 */
	public static String getNum(int count) {
		if (count <= 0) {
			return "";
		}
		StringBuilder s = new StringBuilder();
		for (int i = 0; i < count; i++) {
			s.append(new Random().nextInt(10));
		}
		String string = s.toString();
		// 如果第一位数字为0,则替换为1
		return string.charAt(0) == '0' ? "1" + string.substring(1) : string;
	}

	/**
	 * 获取UUID
	 */
	public static String getUUID() {
		return UUID.randomUUID().toString();
	}

	/**
	 * 字符串替换,将 source 中的 oldString 全部换成 newString
	 *
	 * @param source    源字符串
	 * @param oldString 老的字符串
	 * @param newString 新的字符串
	 * @return 替换后的字符串
	 * 主要用于输入的表单字符串转化成HTML格式的文本
	 */
	public static String Replace(String source, String oldString, String newString) {
		StringBuffer output = new StringBuffer();
		int lengthOfSource = source.length(); // 源字符串长度
		int lengthOfOld = oldString.length(); // 老字符串长度
		int posStart = 0; // 开始搜索位置
		int pos; // 搜索到老字符串的位置
		while ((pos = source.indexOf(oldString, posStart)) >= 0) {
			output.append(source.substring(posStart, pos));
			output.append(newString);
			posStart = pos + lengthOfOld;
		}
		if (posStart < lengthOfSource) {
			output.append(source.substring(posStart));
		}
		return output.toString();
	}

	/**
	 * 将传入的字符串形式的数字,累加1后以字符串的形式返回
	 * @return 累加1后的字符串
	 *  传入的必须是数字
	 */
	public static String comulationOne(String num) {
		long parseLong = Long.parseLong(num);
		return String.valueOf((parseLong + 1));
	}

	/**
	 * @param key   String
	 * @param value String
	 * @return HashMap<String, String>
	 */
	public static Map <String, String> toMap(String key, String value) {
		Map <String, String> map = new LinkedHashMap <String, String>();
		map.put(key, value);
		return map;
	}

	/**
	 * @param key   String
	 * @param value Object
	 * @return Map<String, Object>
	 */
	public static Map <String, Object> toMap(String key, Object value) {
		Map <String, Object> map = new LinkedHashMap <String, Object>();
		map.put(key, value);
		return map;
	}

	/**
	 * 判断字符串是否为int类型数据
	 */
	public static boolean isInt(String str) {
		try {
			Integer.parseInt(str);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 判断字符串是否为long类型数据
	 */
	public static boolean isLong(String str) {
		try {
			Long.parseLong(str);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

}